package com.xxxx.server;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.rabbitmq.client.Channel;
import com.xxxx.server.pojo.Employee;
import com.xxxx.server.pojo.MailConstants;
import com.xxxx.server.pojo.MailLog;
import com.xxxx.server.service.IMailLogService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.amqp.support.AmqpHeaders;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.mail.MailProperties;
import org.springframework.data.redis.core.HashOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.mail.javamail.MimeMessageHelper;
import org.springframework.messaging.Message;
import org.springframework.messaging.MessageHeaders;
import org.springframework.stereotype.Component;
import org.thymeleaf.TemplateEngine;
import org.thymeleaf.context.Context;

import javax.mail.MessagingException;
import javax.mail.internet.MimeMessage;
import java.io.IOException;
import java.util.Date;
import java.util.List;

@Component
public class MailReceiver {
    private static final Logger log = LoggerFactory.getLogger(MailReceiver.class);

    // 注入邮件发送
    @Autowired
    private JavaMailSender javamailSender;

    // 注入邮件配置
    @Autowired
    private MailProperties mailProperties;

    // 注入thymeleaf引擎
    @Autowired
    private TemplateEngine templateEngine;

    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private IMailLogService mailLogService;

    // 发信息
    @RabbitListener(queues = "mail.welcome")
    private void setupMail(Employee employee) {
        MimeMessage msg = javamailSender.createMimeMessage();
        MimeMessageHelper helper = new MimeMessageHelper(msg);
        try {
            // 发件人
            helper.setFrom(mailProperties.getUsername());
            // 收件人
            helper.setTo(employee.getEmail());
            // 主题
            helper.setSubject("入职欢迎邮件");
            // 发送日期
            helper.setSentDate(new Date());
            // 邮件内容
            Context context = new Context();
            context.setVariable("name",employee.getName());
            context.setVariable("posName", employee.getPosition().getName());
            context.setVariable("jobLevelName", employee.getJoblevel().getName());
            context.setVariable("departmentName", employee.getDepartment().getName());
            // 通过thymeleaf引擎去拿到mail.html模板, 将值放进去
            String mail = templateEngine.process("mail", context);
            // 设置发送内容  true: 确定是否为HTML格式
            helper.setText(mail,true);
            // 发送邮件
            javamailSender.send(msg);
        } catch (MessagingException e) {
            log.error("邮件内容设置异常!==>{}", e.getMessage());
        }
    }

    /**
     * 1. 接收到消息时先去redis中确认特定key的hash的key是否存在
     * * 1.1 不存在时,正常走流程: 发送消息-->将已发送的消息的ID作为redis中特定key的hash的key存入redis
     * * 1.2 存在时, 消息已经被消费, 手动确认消息
     * * 2. 当出现异常时, 根据我们的设置手动确认消息,并且设置消息没有被确认时是否退回到队列
     */
    @RabbitListener(queues = MailConstants.MAIL_QUEUE_NAME)
    public void handler(Message message, Channel channel) throws IOException {
        // 从消息中拿到员工类
        Employee employee = (Employee) message.getPayload();
        MessageHeaders header = message.getHeaders();
        // 消息序号  tag需要我们手动确认的时候返回
        long tag = (long) header.get(AmqpHeaders.DELIVERY_TAG);
        // 获取msgId   固定值; spring_returned_message_correlation
        String msgId = (String) header.get("spring_returned_message_correlation");
        // 存入redis
        // 使用hash,因为我们自己准备了一个redis的key,hash的key就是msgId,hash的value无所谓,我们主要需要的就是msgId
        HashOperations hashOperations = redisTemplate.opsForHash();
        // 拿出MailLog的数据 以便进一步判断
        List<MailLog> list = mailLogService.list(new QueryWrapper<MailLog>()
                .eq("msgId", msgId));
        list.forEach(mailLog -> {
            // 先判断他的状态，不然发布没确认了，这里消费却回调了，导致callback运行，status变为已投递。（小林发现的终极bug！！！！！）
            if (mailLog.getStatus() == MailConstants.SUCCESS) {
                // 当再次接收到一模一样的消息, 判断redis中key为mail_log的hash的key是否存在
                if (hashOperations.entries("mail_log").containsKey(msgId)) {
                    log.info("消息已经被消费====>{}", msgId);
                    /**
                     * 手动确认消息
                     * tag: 消息序号
                     * multiple: 是否确认多条
                     * */
                    try {
                        channel.basicAck(tag, false);
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                    return;
                }
            }
        });
        try {
            // 创建消息
            MimeMessage mimeMessage = javamailSender.createMimeMessage();
            MimeMessageHelper helper = new MimeMessageHelper(mimeMessage);
            // 设置邮件内容
            setupMail(helper, employee);
            // 发送邮件
            javamailSender.send(mimeMessage);
            log.info("邮件已发送至[" + employee.getName() + "](" + employee.getEmail() + ").");
            // 将消息ID存入redis
            // 前面90行, 这里自定义一个key,hash的key就是msgId,hash的value随意
            // 这里的重点就是将msgId存进去
            hashOperations.put("mail_log", msgId, "OK");
            // 手动确认消息   是否一次性确认多条: false
            channel.basicAck(tag, false);
        } catch (IOException e) {
            try {
                /**
                 * 手动确认消息
                 * tag: 消息序号
                 * multiple: 是否确认多条
                 * requeue: 消息没有被确认时是否退回到队列
                 */
                channel.basicNack(tag, false, true);
            } catch (IOException ioException) {
                log.error("邮件发送失败====>{}", e.getMessage());
            }
            log.error("邮件发送失败====>{}", e.getMessage());
        }
    }
    /**
     * 设置邮件内容
     */
    private void setupMail(MimeMessageHelper helper, Employee employee) {
        try {
            // 设置发件人
            helper.setFrom(mailProperties.getUsername());
            // 设置收件人  设置员工的email为收件人
            helper.setTo(employee.getEmail());
            // 设置主题
            helper.setSubject(employee.getName() + "录用意向书");
            // 设置发送日期  当前日期
            helper.setSentDate(new Date());
            // 模板内容
            Context context = new Context();
            // 设置内容属性
            context.setVariable("name", employee.getName());
            context.setVariable("posName", employee.getPosition().getName());
            context.setVariable("jobLevelName", employee.getJoblevel().getName());
            context.setVariable("departmentName", employee.getDepartment().getName());
            // 通过thymeleaf引擎去拿到mail.html模板, 将值放进去
            String mail = templateEngine.process("mail", context);
            // 设置发送内容  true: 确定是否为HTML格式
            helper.setText(mail, true);
        } catch (MessagingException e) {
            log.error("邮件内容设置异常!==>{}", e.getMessage());
        }
    }

}
